home *** CD-ROM | disk | FTP | other *** search
- /*
- HASFiles.c from Hsoi's App Shell © 1995-1997 John C. Daub. All rights reserved.
-
- HASFiles.c deals with a lot of the "upper level" file handling functions...the
- related menu commands, read/write, etc. You can also find some other file related
- utility functions in HASUtilFiles.c
-
- */
-
- /*
- question:
-
- Also, after we open a file, should we close it? For some things that don't really
- matter (like dragging a file and reading in it's contents, sure, close it afterwards).
- But for things like opening a file that we're gonna edit, should we close it? Other
- people could always end up opening it (like if file shared), and that could cause all
- sorts of problems...hmm
-
- */
-
- #pragma mark ••• #includes •••
-
- #ifndef _WASTE_
- #include "WASTE.h"
- #endif
- #include "HASGlobals.h"
- #ifndef __HSOIS_APP_SHELL__
- #include "HASMain.h"
- #endif
- #include "HASDialogs.h"
- #include "HASFiles.h"
- #include "HASUtilCursors.h"
- #include "HASUtilFiles.h"
- #include "HASWindows.h"
- #include "HASUtilities.h"
- #include "HASUtilPStrings.h"
-
- #include "WASTE_Objects.h"
-
-
- #pragma mark -
- #pragma mark ••• High Level Routines •••
-
- /*
- * Simply enough, this is the function called when the user wants to open a document
- * via the File:Open (cmd-O) command
- */
-
- OSErr HsoiDoOpen( void )
- {
- StandardFileReply reply;
- SFTypeList typeList;
- OSErr retval = noErr;
- Point where;
- ModalFilterYDUPP myGetFileFilter;
-
- // if we have a sound playing, stop it before we proceed
-
- if ( SoundIsPlaying() )
- StopCurrentSound();
-
- // set up a list of file types we can open for GetFile
- // in this case, all we can open are text files
-
- typeList[0] = TYPE_TEXT;
- typeList[1] = TYPE_TEXT_READ_ONLY;
-
-
- // if Point where is -1, -1, CustomGetFile will autocenter the dialog
-
- where.h = -1;
- where.v = -1;
-
- // put up the standard open dialog box
- // (we use CustomGetFile instead of StandardGetFile because we want to provide
- // our own dialog filter procedure that takes care of updating our windows)
-
- // get our custom filter
-
- myGetFileFilter = NewModalFilterYDProc(hsoiMySFDialogFilter);
-
- // do the dialog
-
- CustomGetFile( nil, 2, typeList, &reply, 0, where, nil, myGetFileFilter, nil, nil, nil );
-
- // dump the filter
-
- DisposeRoutineDescriptor( myGetFileFilter );
-
- // if the user okayed the dialog, create a new window from the specified file
-
- if ( reply.sfGood )
- retval = HsoiCreateWindow( &reply.sfFile );
- else
- retval = userCanceledErr;
-
- return retval;
- }
-
- /*
- * this is the "lower level" function that deals with actually saving a window's
- * contents (this is called by DoSave and DoSaveAs)
- */
-
- OSErr HsoiSaveWindow( const FSSpec *pFileSpec, WindowRef window )
- {
- OSErr err;
- AliasHandle aHandle;
- DocumentHandle hDocument;
-
- // get a handle to our window's document record
-
- hDocument = HsoiGetWindowDocument(window);
-
- // forget the existing handle cause we're gonna create a new one for it.
-
- HsoiForgetHandle( &(*hDocument)->fileAlias );
-
- // save text
-
- err = HsoiWriteTextFile( pFileSpec, false, (*hDocument)->we );
- if ( err == noErr )
- {
- // set the window title to the file name
-
- SetWTitle( window, pFileSpec->name );
-
- // replace the old window alias (if any) with a new one created from pFileSpec
-
- HsoiForgetHandle( &(*hDocument)->fileAlias );
- err = NewAlias( nil, pFileSpec, &aHandle );
- // if err, aHandle will be nil, and it's not fatal,
- // just will make subsequent saves a pain
- (*hDocument)->fileAlias = (Handle)aHandle;
-
- // incidentally, we could do the call to NewAlias and put the new alias in
- // the document record in one line instead of 2...like this:
-
- // err = NewAlias( nil, pFileSpec, (AliasHandle *) &(*hDocument)->fileAlias);
-
- // but i just didn't want to do that cause of all the typecasting and stuff.
- // typecasting, especially of this nature (with all the dereferencing, etc.)
- // does generate better code (performance wise), but is not as readable.
- }
-
- return err;
- }
-
- /*
- * HsoiDoSaveAs() is called when the user selects "File:Save As" or selects "File:Save"
- * on a new, untitled document. It prompts the user for a place to save it and does so
- */
-
-
- OSErr HsoiDoSaveAs( const FSSpec *suggestedTarget, WindowRef window )
- {
- StringHandle hPrompt;
- Str255 defaultName;
- StandardFileReply reply;
- OSErr retval = noErr;
- Point where;
- ModalFilterYDUPP filterProc;
-
- // if we have a sound playing, stop it before we proceed
-
- if ( SoundIsPlaying() )
- StopCurrentSound();
-
- // coordinates for where to place the putfile dialog...if -1,-1, the dialog will
- // be autocentered on the screen
-
- where.v = -1;
- where.h = -1;
-
- // get the prompt string for CustomPutFile from a 'STR ' resource and lock it
-
- hPrompt = GetString( strPutFilePrompt );
-
- // not thoroughly necesary to handle the error here...
-
- // if ( hPrompt == nil )
- // {
- // HsoiDoError( rErrorStrings, errFailGetResource, err, kNoteAlert );
- // return err;
- // }
-
- // lock the prompt down
-
- HLockHi( (Handle)hPrompt );
-
- // if a suggested target file is provided, use its name as the default name
-
- if ( suggestedTarget != nil )
- {
- HsoipStringCopy( suggestedTarget->name, defaultName );
- HsoiSetDefaultDirectory( suggestedTarget );
- }
- else //otherwise use the window title as the default name for CustomPutFile
- {
- GetWTitle( window, defaultName );
- }
-
- // put up the standard save dialog box
-
- // get our dialog filter
-
- filterProc = NewModalFilterYDProc(hsoiMySFDialogFilter);
-
- // do the dialog
-
- CustomPutFile( *hPrompt, defaultName, &reply, 0, where, nil, filterProc, nil, nil, nil );
-
- // dump the filter
-
- DisposeRoutineDescriptor( filterProc );
-
- // unlock the string resource
-
- HUnlock( (Handle)hPrompt );
-
- // if the user okayed the dialog, save the window to he specified file
-
- if ( reply.sfGood )
- {
- retval = HsoiSaveWindow( &reply.sfFile, window );
- }
- else
- retval = userCanceledErr;
-
- return retval;
- }
-
-
- /*
- * This is what's called if the user selects "File:Save". if a new document, then
- * HsoiDoSaveAs is called to prompt for a new location, else HsoiSaveWindow is called
- * to easily and quickly save the file.
- */
-
- OSErr HsoiDoSave( WindowRef window )
- {
- FSSpec spec;
- FSSpecPtr suggestedTarget = nil;
- Boolean promptForNewFile = true, aliasTargetWasChanged;
- OSErr retval = noErr;
-
- // resolve the alias associated with this window, if any
-
- if ( (*HsoiGetWindowDocument(window))->fileAlias != nil )
- {
- if ( ( ResolveAlias( nil, ((AliasHandle)(*HsoiGetWindowDocument(window))->fileAlias),
- &spec, &aliasTargetWasChanged ) == noErr ) )
- {
- if ( aliasTargetWasChanged )
- suggestedTarget = &spec;
- else
- promptForNewFile = false;
- }
- }
-
- // if no file has been previously associated with this window, or if the
- // alias resolution has failed, or if the alias target was changed
- // prompt the user for a new destination
-
- if ( promptForNewFile )
- retval = HsoiDoSaveAs( suggestedTarget, window );
- else
- {
- // if we have a sound playing, stop it before we proceed
-
- if ( SoundIsPlaying() )
- StopCurrentSound();
-
- // save the file
-
- retval = HsoiSaveWindow( &spec, window );
- }
-
- return retval;
- }
-
- // DoRevert will revert the contents of the window (the "file") back to the most recent
- // saved version of the document.
-
- // there is not really a need to check the window to make sure it's got a hWE instance
- // (i.e. not a dialog window, etc) cause in AdjustMenus, the only time Revert is Enabled
- // is if the window is a proper one. Furthermore, due to this, we don't have to check
- // the vaildity of the AliasHandle in the DocumentRecord
-
- OSErr HsoiDoRevert( WindowRef theWindow )
- {
- short itemHit;
- Str255 windowTitle;
- FSSpec targetFile;
- Boolean aliasChanged;
- OSErr err = noErr; // assume success
-
- // if we have a sound playing, stop it before we proceed
-
- if ( SoundIsPlaying() )
- StopCurrentSound();
-
- // set the cursor to the arrow. we call InitCursor() instead of like
- // SetCursor( &qd.arrow ) cause InitCursor will make sure that if the cursor
- // is hidden, it'll be shown.
-
- InitCursor();
-
- // get the title of the window for the Revert alert
-
- GetWTitle( theWindow, windowTitle );
-
- // set up the Alert with the window title
-
- ParamText( windowTitle, NIL_STRING, NIL_STRING, NIL_STRING );
-
- // do the alert
-
- itemHit = CautionAlert( rRevertAlert, HsoiGetMyStandardDialogFilter() );
-
- // if itemHit was the OK button (1), they want to revert...if !OK, don't revert
-
- if ( itemHit != ok )
- return err;
- else
- {
- // resolve the alias to get the FSSpec
-
- err = ResolveAlias( nil, (AliasHandle)(*HsoiGetWindowDocument(theWindow))->fileAlias, &targetFile, &aliasChanged );
- if ( err != noErr )
- {
- // there are a lot of different error codes that ResolveAlias returns...some fatal,
- // some not...if you'd like to do more robust error handling, feel free to
-
- HsoiDoError( rErrorStrings, errFailResolveAlias, err, kErrStop );
-
- return err;
- }
-
- // axe the old stuff
-
- HsoiDestroyWindow( theWindow );
-
- // create a new window with the "old" info in it
-
- err = HsoiCreateWindow( &targetFile );
- if ( err != noErr )
- {
- HsoiDoError( rErrorStrings, errCantCreateWindow, err, kErrStop );
- return err;
- }
-
- }
-
- // made it this far? we can return noErr
-
- return noErr;
- }
-
-
- #pragma mark -
- #pragma mark ••• Low Level Routines •••
-
- /*
- * HsoiReadTextFile() takes care of actually opening up a file and reading it's
- * contents into a window
- */
-
-
- OSErr HsoiReadTextFile( const FSSpec *pFileSpec, WEReference we )
- {
- Size textSize;
- OSErr err = noErr;
- short dataForkRefNum = 0;
- short resForkRefNum = 0;
- Handle hText = nil;
- StScrpHandle hStyles = nil;
- WESoupHandle hSoup = nil;
- Boolean stoppedSpinning = false; // did we call HsoiStopVBLSpinning in error handling
- // or not?
-
- // if we have a sound playing, stop it before we proceed. hopefully
- // the sound will have been stopped (if any) prior to here, but just in case
- // things didn't work, we'll have this for added "security"
-
- if ( SoundIsPlaying() )
- StopCurrentSound();
-
-
- // since this might be a lengthy operation, let's spin the cursor with a VBL task
- // as a note, with all our error handling, if we do hit an error, we'll first
- // stop the spinning cursor the do the error.
-
- HsoiStartVBLSpinning();
-
- // open the data fork with read-only permission
-
- err = FSpOpenDF( pFileSpec, fsRdPerm, &dataForkRefNum );
- if ( err != noErr )
- {
- HsoiStopVBLSpinning();
- stoppedSpinning = true;
- HsoiDoError( rReadErrorStrings, strRdTxFSpOpenDF, err, kErrStop );
- goto cleanup;
- }
-
- // get data fork size
-
- err = GetEOF( dataForkRefNum, &textSize );
- if ( err != noErr )
- {
- HsoiStopVBLSpinning();
- stoppedSpinning = true;
- HsoiDoError( rReadErrorStrings, strRdTxGetEOF, err, kErrStop );
- goto cleanup;
- }
-
- // set the position in the file from where to start reading
-
- err = SetFPos( dataForkRefNum, fsFromStart, 0L );
- if ( err != noErr )
- {
- HsoiStopVBLSpinning();
- stoppedSpinning = true;
- HsoiDoError( rReadErrorStrings, strRdTxSetFPos, err, kErrStop );
- goto cleanup;
- }
-
-
- // Try to allocate a handle that large, use temporary memory if available
-
- err = HsoiNewHandleTemp( textSize, &hText );
-
- if ( err || (StripAddress( *hText ) == 0L) )
- {
- HsoiStopVBLSpinning();
- stoppedSpinning = true;
- HsoiDoError( rReadErrorStrings, strRdTxNewHandle, err, kErrStop );
- goto cleanup;
- }
-
-
- // read in the text
-
- HLock( hText );
- err = FSRead( dataForkRefNum, &textSize, *hText );
- HUnlock( hText );
- if ( err != noErr )
- {
- HsoiStopVBLSpinning();
- stoppedSpinning = true;
- HsoiDoError( rReadErrorStrings, strRdTxFSRead, err, kErrStop );
- goto cleanup;
- }
-
- // let's close the data fork before opening up the resource fork.
-
- err = FSClose( dataForkRefNum );
- if ( err != noErr )
- {
- HsoiStopVBLSpinning();
- stoppedSpinning = true;
- HsoiDoError( rReadErrorStrings, strRdTxFSClose, err, kErrStop );
- goto cleanup;
- }
- else
- dataForkRefNum = 0;
-
- // see if the file has a resource fork
-
- resForkRefNum = FSpOpenResFile( pFileSpec, fsCurPerm );
- if ( resForkRefNum != -1 ) //FSpOpenResFile will return -1 if it fails
- {
- // look for a style scrap resource (get the first one; the resource ID doesn't matter)
-
- // this whole resource stuff is interesting. first, we could have no resource fork
- // at all (and that's easy to deal with). second, we could have a resource fork
- // and the resources we want ('styl' and 'SOUP'). but third, we could have
- // a resource fork but not the resources we want. it's simple to deal with all these
- // things, but just make sure that you do!
-
- hStyles = (StScrpHandle)Get1IndResource( TYPE_STYLE, 1 );
- err = ResError();
- if ( (err != noErr) && (err != resNotFound) )
- {
- // if we have some strange, funky error
-
- HsoiStopVBLSpinning();
- stoppedSpinning = true;
- hStyles = nil;
- HsoiDoError( rReadErrorStrings, strRdTxGetStyleH, err, kErrStop );
- goto CRF;
- }
- else if ( err == resNotFound )
- {
- // have a resource fork, but just not a 'styl' resource
-
- hStyles = nil;
- }
- else
- // got it!
- DetachResource( (Handle)hStyles );
-
- // look for a soup resource as well
- hSoup = (WESoupHandle)Get1IndResource( TYPE_SOUP, 1 );
- err = ResError();
- if ( (err != noErr) && (err != resNotFound) )
- {
- // some funky error
-
- HsoiStopVBLSpinning();
- stoppedSpinning = true;
- hSoup = nil;
- HsoiDoError( rReadErrorStrings, strRdTxGetSoupH, err, kErrStop );
- goto CRF;
- }
- else if ( err == resNotFound )
- {
- // just no 'SOUP' resource
-
- hSoup = nil;
- }
- else
- // got it!
-
- DetachResource( (Handle)hSoup );
-
- CRF:
- CloseResFile( resForkRefNum );
- err = ResError();
- if ( err != noErr )
- {
- HsoiStopVBLSpinning();
- stoppedSpinning = true;
- HsoiDoError( rReadErrorStrings, strRdTxCloseResFile, err, kErrStop );
- goto cleanup;
- }
- else
- resForkRefNum = 0;
-
- }
- else
- {
- // does not have resource fork, so no style or soup
-
- hStyles = nil;
- hSoup = nil;
- }
-
-
- // insert the text into the WE record
-
- // don't lock (HLock) the style and soup handles here....it's not
- // really necessary to do, but also, there is a chance that the
- // handles could be nil (no resource fork for the file). passing
- // nil to the Memory Manager can be a BIG problem. Thanx to
- // Chris Thomas <mailto:ckt@best.com> for the fix
-
- HLock( hText );
- err = WEInsert( *hText, textSize, hStyles, hSoup, we );
- HUnlock( hText );
-
- if ( err != noErr )
- {
- HsoiStopVBLSpinning();
- stoppedSpinning = true;
- HsoiDoError( rReadErrorStrings, strRdTxWEInsert, err, kErrStop );
- goto cleanup;
- }
-
-
- // set the insertion point at the beginning of the text
- WESetSelection( 0, 0, we );
-
- // reset the WE instance modification count
- WEResetModCount( we );
-
- // clean up and exit
-
- err = noErr; // if we made it this far, there were no errors
-
- cleanup:
-
- // dump our handles so we don't hog nor fragment memory with stuff we don't
- // need anymore
-
- HsoiForgetHandle(&hText);
- HsoiForgetHandle((Handle *)&hStyles);
- HsoiForgetHandle((Handle *)&hSoup);
-
- // close the file forks
-
- if ( dataForkRefNum > 0 )
- {
- FSClose(dataForkRefNum);
- dataForkRefNum = 0;
- }
-
- if ( resForkRefNum > 0 )
- {
- CloseResFile(resForkRefNum);
- resForkRefNum = 0;
- }
-
- // and stop spinning our cursor
-
- if ( !stoppedSpinning )
- HsoiStopVBLSpinning();
-
- return err;
- }
-
-
- /*
- * HsoiWriteTextFile is my "low level" routine to handle the actual writing of data
- * from window to disk file.
- *
- * this contains many "proper" ways to save data to disk: checking for locked files,
- * using temp files, using name strings, etc. This is a very safe and recommended
- * way to save data to disk (to prevent sh*t from happening)
- *
- */
-
- OSErr HsoiWriteTextFile( const FSSpec *pFileSpec, Boolean isReadOnly, WEReference we )
- {
- FInfo fileInfo;
- Size textSize;
- Boolean replacing = false;
- OSErr err = noErr;
- short dataForkRefNum = 0;
- short resForkRefNum = 0;
- Handle hText = nil;
- StScrpHandle hStyles = nil;
- WESoupHandle hSoup = nil;
- Handle strHandle; // these 4 variables are used to copy the name
- Str255 strName; // string resource from the app's resource fork
- ResType strType; // into the file's resource fork. see below for
- short strID; // more details.
- unsigned long theTime;
- Str255 tempFileName;
- FSSpec tempFileSpec;
- short tempVRef; // volume reference # for the temp file
- long tempDirID; // directory ID of the temp file
- Boolean stoppedSpinning = false;
-
- // if we have a sound playing, stop it before we proceed. hopefully, the
- // sound (if any) was stopped before we got to here, but just in case,
- // we'll have this here for added security.
-
- if ( SoundIsPlaying() )
- StopCurrentSound();
-
- // this could take a while to do so let's spin the cursor with a VBL cursor
-
- HsoiStartVBLSpinning();
-
- // will we be replacing an existing file?
-
- err = FSpGetFInfo( pFileSpec, &fileInfo );
- if ( err == noErr )
- replacing = true;
- else if ( err == fnfErr )
- replacing = false;
- else
- {
- HsoiStopVBLSpinning();
- stoppedSpinning = true;
- HsoiDoError( rWriteErrorStrings, strWrTxFFSpGetInfo, err, kErrNote );
- goto cleanup;
- }
-
- // if the file currently exists (meaning it was saved at some time in the past, be it
- // 5 minutes ago, or 5 days ago), there is a chance that it could be locked (from the
- // Finder's "Get Info" window (or otherwise). If the file is locked, you cannot
- // save changes. Therefore, if 'replacing' is true, we have to throw up a message
- // saying that we can't do this, then exit.
-
- if ( replacing )
- {
- // check if the file is locked
-
- err = HsoiFSpCheckObjectLock( pFileSpec );
- if ( err != noErr ) // if it returns noErr, the file/directory is NOT locked
- {
- short itemHit;
- Str255 errString;
-
- // stop our VBL cursor from spinning
-
- HsoiStopVBLSpinning();
- stoppedSpinning = true;
-
- // give us an arrow cursor
-
- //SetCursor( &qd.arrow );
- InitCursor();
-
- // change our error number to a string for ease of display
-
- NumToString( (long)err, errString );
-
- // get the text in the dialog set up
-
- ParamText( pFileSpec->name, errString, NIL_STRING, NIL_STRING );
-
- // and blab it out
-
- itemHit = StopAlert( rFileLockedAlert, HsoiGetMyStandardDialogFilter() );
-
- return err;
- }
- }
-
- // the technique we'll take to save/write our data will be to use temporary files.
- // this is a handy thing and recommended way to save data. why? well, the "usual"
- // way to save would be to just write; if it's a new file, just do it. but if
- // we're like writing data to a file that already exists (like you're saving as
- // you go along working), to just write the new data to the old file could prove
- // to be disasterous. What if during the write of the new data there was a fubar?
- // your old, original file could be ruined and no way to recover old work! not
- // a good thing. Sure, this method isn't failsafe (like when you do the actual
- // exhanging of the files, something might go wrong), but in my opinion (and
- // the opinion of Apple and other developers), this is the best thing you can
- // do to ensure your data will be safe if something goes wrong in the write.
-
- // so, here's what we do. if the file already exists (replacing == true), we'll
- // write our new data to a temporary file (just a "scrap" thing that we don't
- // care about and after we're done, who cares what happens to it). Then, if
- // the new write was successful, just switch the new file into the old file's
- // place, and viola! it's seemless and nice "behind the scenes" sorta stuff
- // that your user will never know about, but will probably come to appreciate
- // some day :)
-
- if ( replacing )
- {
-
- // create the temporary file name. the name doesn't have to make sense, just
- // be unique. incidentally, if you're worried that perhaps two or more running
- // applications will call GetDateTime at the same time and then generate the
- // same temp file name, well...it might happen, but it's pretty darn slim.
- // if you're really worried (especially if you're working on an app that might
- // be used in a networked environment), i'm sure you can think up more ways
- // to ensure a totally 100% unique filename
-
- GetDateTime( &theTime );
- NumToString( theTime, tempFileName );
-
- // find the temporary items folder on the file's volume; create it if necessary
- // it is important that the temp folder (and the temp file) and the "original" target
- // file be on the same volume; if not, FSpExchangeFiles will return diffVolErr (-1303)
- // and won't work
-
- err = FindFolder( pFileSpec->vRefNum, kTemporaryFolderType, kCreateFolder, &tempVRef, &tempDirID );
- if ( err != noErr )
- {
- HsoiStopVBLSpinning();
- stoppedSpinning = true;
- HsoiDoError( rWriteErrorStrings, strWrTxFindFolder, err, kErrStop );
- goto cleanup;
- }
-
- // make an FSSpec for the temp file
-
- err = FSMakeFSSpec( tempVRef, tempDirID, tempFileName, &tempFileSpec );
- if ( (err != noErr) && (err != fnfErr ) )
- {
- HsoiStopVBLSpinning();
- stoppedSpinning = true;
- HsoiDoError( rWriteErrorStrings, strWrTxFSMakeFSSpec, err, kErrStop );
- goto cleanup;
- }
-
- }
-
- // create a new file. if we're replacing, make a temp file. if it's a
- // new file from the onset, just create the file
-
- // if the user wants to make this file a read-only file (a type 'ttro'), then
- // we do that accordingly..else, just a regular 'TEXT' file. Currently, the
- // only place this is "relevant" is in the Help dialog when the user
- // saves the help text (it will output a read-only file). However, if you'd
- // like to allow the user to save regular document files as TEXT or ttro,
- // you'll have to go back to the CustomPutFile() call, put in a means to
- // get this info from the user (maybe 2 radio buttons or a small popup menu),
- // and then pass this info on to here.
-
- if ( replacing )
- FSpCreateResFile( &tempFileSpec, 'trsh', 'trsh', smSystemScript );
- else
- {
- if ( isReadOnly )
- FSpCreateResFile( pFileSpec, APP_SIGNATURE, TYPE_TEXT_READ_ONLY, smSystemScript );
- else
- FSpCreateResFile( pFileSpec, APP_SIGNATURE, TYPE_TEXT, smSystemScript );
- }
-
- err = ResError();
- if ( err != noErr )
- {
- HsoiStopVBLSpinning();
- stoppedSpinning = true;
- HsoiDoError( rWriteErrorStrings, strWrTxFFSpCrRsF, err, kErrNote );
- goto cleanup;
- }
-
- // if replacing an old file, copy the old file information
- if ( replacing )
- {
- err = FSpSetFInfo( &tempFileSpec, &fileInfo );
- if ( err != noErr )
- {
- HsoiStopVBLSpinning();
- stoppedSpinning = true;
- HsoiDoError( rWriteErrorStrings, strWrTxFFSpSetFInfo, err, kErrNote );
- goto cleanup;
- }
- }
-
-
- // open the data fork for writing
- if ( replacing )
- err = FSpOpenDF( &tempFileSpec, fsCurPerm, &dataForkRefNum );
- else
- err = FSpOpenDF( pFileSpec, fsCurPerm, &dataForkRefNum );
-
- if ( err != noErr )
- {
- HsoiStopVBLSpinning();
- stoppedSpinning = true;
- HsoiDoError( rWriteErrorStrings, strWrTxFFSpOpenDF, err, kErrNote );
- goto cleanup;
- }
-
- // set the end-of-file
-
- err = SetEOF( dataForkRefNum, nil );
- if ( err != noErr )
- {
- HsoiStopVBLSpinning();
- stoppedSpinning = true;
- HsoiDoError( rWriteErrorStrings, strWrTxFSetEOF, err, kErrNote );
- goto cleanup;
- }
-
- // set the position in the file to write from
-
- err = SetFPos( dataForkRefNum, fsFromStart, nil );
- if ( err != noErr )
- {
- HsoiStopVBLSpinning();
- stoppedSpinning = true;
- HsoiDoError( rWriteErrorStrings, strWrTxFSetFPos, err, kErrNote );
- goto cleanup;
- }
-
-
-
- // get the text handle from the WE instance
- // WEGetText returns the original handle, not a copy, so don't dispose of it!!
- hText = WEGetText( we );
- textSize = GetHandleSize( hText );
-
- // write the text
- HLock( hText );
- err = FSWrite( dataForkRefNum, &textSize, *hText );
- HUnlock( hText );
-
- if ( err != noErr )
- {
- HsoiStopVBLSpinning();
- stoppedSpinning = true;
- HsoiDoError( rWriteErrorStrings, strWrTxFFSWrite, err, kErrNote );
- goto cleanup;
- }
-
- // open the resource file for writing
-
- if ( replacing )
- resForkRefNum = FSpOpenResFile( &tempFileSpec, fsRdWrPerm );
- else
- resForkRefNum = FSpOpenResFile( pFileSpec, fsRdWrPerm );
-
- err = ResError();
- if ( err != noErr )
- {
- HsoiStopVBLSpinning();
- stoppedSpinning = true;
- HsoiDoError( rWriteErrorStrings, strWrTxFFSpOpenResFile, err, kErrNote );
- goto cleanup;
- }
-
- // allocate temporary handles to hold the style scrap and the soup
- // try tapping temporary memory since WECopyRange() could get huge
-
- err = HsoiNewHandleTemp( 0, (Handle *)&hStyles );
- if ( err != noErr )
- {
- HsoiStopVBLSpinning();
- stoppedSpinning = true;
- HsoiDoError( rWriteErrorStrings, strWrTxFstylMemErr, err, kErrNote );
- goto cleanup;
- }
-
- err = HsoiNewHandleTemp( 0, (Handle *)&hSoup );
- if ( err != noErr )
- {
- HsoiStopVBLSpinning();
- stoppedSpinning = true;
- HsoiDoError( rWriteErrorStrings, strWrTxFsoupMemErr, err, kErrNote );
- goto cleanup;
- }
-
- // create the style scrap and the soup
-
-
- err = WECopyRange( 0, MAXLONG, nil, hStyles, hSoup, we );
- if ( err != noErr )
- {
- HsoiStopVBLSpinning();
- stoppedSpinning = true;
- HsoiDoError( rWriteErrorStrings, strWrTxFWECopyRange, err, kErrNote );
- goto cleanup;
- }
-
- // make them resource handles. AddResource doesn't actually write the resource
- // out to the file, it just converts a "regular old" handle to a handle that
- // the Resource Manager can recognize (makes it into a resource handle). FYI:
- // the opposite of this (convert a resource handle to a regular handle) is
- // DetachResource(). See IM:More Macintosh Toolbox (the Resource Manager stuff)
- // for more information.
-
- AddResource( (Handle)hStyles, TYPE_STYLE, 128, NIL_STRING );
- err = ResError();
- if ( err != noErr )
- {
- HsoiStopVBLSpinning();
- stoppedSpinning = true;
- HsoiDoError( rWriteErrorStrings, strWrTxFstylAddRes, err, kErrNote );
- goto cleanup;
- }
-
- AddResource( (Handle)hSoup, TYPE_SOUP, 128, NIL_STRING );
- err = ResError();
- if ( err != noErr )
- {
- HsoiStopVBLSpinning();
- stoppedSpinning = true;
- HsoiDoError( rWriteErrorStrings, strWrTxFsoupAddRes, err, kErrNote );
- goto cleanup;
- }
-
- // write them to the resource file
-
- HLock( (Handle)hStyles );
- ChangedResource( (Handle)hStyles );
- HUnlock( (Handle)hStyles );
- WriteResource( (Handle)hStyles );
- err = ResError();
- if ( err != noErr )
- {
- HsoiStopVBLSpinning();
- stoppedSpinning = true;
- HsoiDoError( rWriteErrorStrings, strWrTxFstylWriteRes, err, kErrNote );
- goto cleanup;
- }
-
- HLock( (Handle)hSoup );
- ChangedResource( (Handle)hSoup );
- HUnlock( (Handle)hSoup );
- WriteResource( (Handle)hSoup );
- err = ResError();
- if ( err != noErr )
- {
- HsoiStopVBLSpinning();
- stoppedSpinning = true;
- HsoiDoError( rWriteErrorStrings, strWrTxfsoupWriteRes, err, kErrNote );
- goto cleanup;
- }
-
- /* Now, let's add the name string to the file. This string ('STR ' -16396) contains
- the name of the application. If a file is "orphaned", the Finder will use this
- string to notify you of the app the belongs to/was created by. Neat huh?
- I wonder why more apps don't do this...
-
- One kicker here tho...we create 'TEXT' files...Teach/SimpleText can open these
- files. So, if your app goes bye-bye, the Finder will "default" to asking if
- they want to open the file up in TeachText/SimpleText. So, you won't get
- your cool name string, but at least people can still read their text.
-
- The following is pretty much taken directly from IM: Toolbox Essentials, 7-29
- */
-
- UseResFile( gAppResourceFork ); // switch the "current open rsrc fork" to that of the app
-
- strHandle = GetResource( 'STR ', strAppNameString ); // get the name string
-
- if ( strHandle != nil )
- {
- GetResInfo( strHandle, &strID, &strType, strName ); // get resource name
- DetachResource( strHandle ); // detach the resource
-
- UseResFile( resForkRefNum ); // swtich back to the file's resource fork.
- AddResource( strHandle, strType, strID, strName ); // add the resource
-
- if ( ResError() == noErr )
- WriteResource( strHandle ); // write the resource handle
-
- }
- // "clean" this document by resetting the WE instance modification count
-
- WEResetModCount( we );
-
- // clean up
-
- // if we made it this far, no errors to return
-
- err = noErr;
-
- cleanup:
-
- // remember, don't dispose the hText handle!
-
- HsoiForgetResource( (Handle *)&hStyles );
- HsoiForgetResource( (Handle *)&hSoup );
- HsoiForgetResource( (Handle *)&strHandle );
-
- if ( dataForkRefNum > 0 )
- {
- FSClose( dataForkRefNum );
- dataForkRefNum = 0;
- }
-
- if ( resForkRefNum > 0 )
- {
- CloseResFile( resForkRefNum );
- resForkRefNum = 0;
- }
-
- if ( replacing && ( err == noErr ) )
- {
- // update the disk with any unwritten data
-
- FlushVol( NIL_STRING, tempFileSpec.vRefNum );
-
- // since we were replacing an existing file, let's now swap the original
- // and the temp file. let's hear it for safe saves.
-
- err = FSpExchangeFiles( &tempFileSpec, pFileSpec );
- if ( err != noErr )
- {
- // handle the error
-
- return err;
- }
-
- // can the temp file since we don't need it anymore
-
- err = FSpDelete( &tempFileSpec );
- if ( err != noErr )
- {
- // handle the error
-
- return err;
- }
- }
-
- // and update the disk with any unwritten data
-
- if ( err == noErr )
- FlushVol( NIL_STRING, pFileSpec->vRefNum );
-
- if ( !stoppedSpinning )
- HsoiStopVBLSpinning();
-
-
- return err;
-
- }
-
-
-
- /*
- HsoiTranslateDrag() is called when a file is dragged over an open application window (i.e
- a file's icon in the Finder is dragged over a window in your application).
-
- Now, I used to have it that if you dragged a file of type 'TEXT' over the window, this
- routine would open up the file and read it in. If it was a file of not-TEXT, then it
- was just ignored.
-
- Then one day, Michael Kamprath's WASTE Object Handlers started to work...the hfs stuff
- started to take over, i still don't know why. So now, whenever something of an hfs
- type (a Finder icon, be it a file/document, or a disk or whatever) is dragged over an
- application window, some FSSpec info about that is saved...if you double click it, it'll
- open whatever you stored in here.
-
- But, i would still like it that if you dragged a text file over the window, to hvae the
- file opened up and inserted. So, what i've done is this: i've commented out most
- of the stuff below (left in just enough stuff to ensure Kamprath's stuff will kick in)
- and then put some TEXT file opening stuff in HsoiMyRecieveHandler().
-
- See that function and also Kamprath's example code in his archive for more info
- */
-
- pascal OSErr HsoiTranslateDrag(DragReference theDrag, ItemReference theItem, FlavorType requestedType, Handle dataHandle)
- {
- #pragma unused ( theDrag, theItem, dataHandle )
-
- OSErr err = badDragFlavorErr; // assume failure
-
- // we'll try to translate HFS objects into TEXT, so make sure that is the requested type
-
- if ( requestedType != TYPE_TEXT )
- return err;
-
- if ( requestedType != TYPE_TEXT_READ_ONLY )
- return err;
-
- err = noErr;
-
- return err;
-
- }
-
-
-